#!/usr/bin/env python2
import os
import sys
import errno
import uuid
import getopt
import subprocess
import time

#docker inspect --format='{{.NetworkSettings.IPAddress}}' 9ba0329ace72
#docker exec CID ifconfig
#(echo "mdsmds";sleep 1;echo "mdsmds") | passwd > /dev/null

path = os.path.abspath(os.path.split(os.path.realpath(__file__))[0])
#dock_list = ['lich_test1', 'lich_test2', 'lich_test3', 'lich_test4', 'lich_test5']
#dock_list = ['lich_test1', 'lich_test2']
#dock_list = ['lich_test1']
tpl = "lich"

sys.path.insert(0, "%s/../admin/" %(path))
from buddha import lsb
from utils import _exec_pipe, _put_remote, _exec_system, _exec_shell, _derror, _get_value, _set_value
from fail import Fail, fail_exit, VALGRIND_CMD, VALGRIND_KEYWORD
from global_variable import dock_list

def usage():
    print ("usage:")
    print (" step1 : " + sys.argv[0] + " --pull")
    print (" step2 : " + sys.argv[0] + " --build")
    print (" step3 : " + sys.argv[0] + " --run")
    print (" step4 : " + sys.argv[0] + " --conf")
    print ("[step5]: " + sys.argv[0] + " --createcluster")
    print ("[step6]: " + sys.argv[0] + " --cleancluster")
    print ("[step7]: " + sys.argv[0] + " --updatecode")
    print (" step8 : " + sys.argv[0] + " --test [--nofail] [--valgrind]")

    print ("other opts:")
    print (sys.argv[0] + " --list")
    print (sys.argv[0] + " --start")
    print (sys.argv[0] + " --stop")
    print (sys.argv[0] + " --remove")

def docker_list():
    for i in dock_list:
        _exec_system("docker  inspect --format='{{.NetworkSettings.IPAddress}}' %s" % (i), False)

def docker_add_config():
    for i in dock_list:
        _exec_system("docker  exec %s mkdir /boot" %(i), False)
        _exec_system("scp /boot/config-`uname -r` `docker inspect --format='{{.NetworkSettings.IPAddress}}' %s`:/boot" % (i) , False)

def docker_pull():
    errno = _exec_system("docker pull centos:7")
    if errno:
        _derror("Please install docker last version")
        exit(errno)

def docker_build():
    userdir = os.path.expanduser('~')
    if not os.path.exists(userdir + "/.ssh/id_dsa")\
            or not os.path.exists(userdir + "/.ssh/id_dsa.pub"):
        _derror("Please generate ssh key file use 'ssh-keygen -t dsa'")
        exit(1)

    _exec_system("cat ~/.ssh/id_dsa > %s/id_dsa" % (path))
    _exec_system("cat ~/.ssh/id_dsa.pub > %s/id_dsa.pub" % (path))
    _exec_system("cat ~/.ssh/id_dsa.pub > %s/authorized_keys" % (path))
    _exec_system("cp %s/../rpms/libisal-2.14.0-1.el7.centos.x86_64.rpm %s/libisal-2.14.0-1.el7.centos.x86_64.rpm" % (path, path))
    _exec_system("cp %s/../rpms/libisal-devel-2.14.0-1.el7.centos.x86_64.rpm %s/libisal-devel-2.14.0-1.el7.centos.x86_64.rpm" % (path, path))

    file_object = open("%s/rsyncd.conf" % (path), 'w')
    file_object.write("[lich]\n    path=/tmp\n    readonly=no\n    list=yes")
    file_object.close( )

    errno = _exec_system("docker build --rm -t %s %s" % (tpl, path))
    if errno:
        _derror("Please install docker last version")
        exit(errno)

    _exec_system("rm %s/id_dsa" % (path))
    _exec_system("rm %s/id_dsa.pub" % (path))
    _exec_system("rm %s/authorized_keys" % (path))
    _exec_system("rm %s/rsyncd.conf" % (path))
    _exec_system("rm %s/libisal-2.14.0-1.el7.centos.x86_64.rpm" % (path))
    _exec_system("rm %s/libisal-devel-2.14.0-1.el7.centos.x86_64.rpm" % (path))

def get_addr(target):
    ip = _exec_pipe(['docker', 'inspect', "--format='{{.NetworkSettings.IPAddress}}'", target], 0, False)[:-1]
    return ip.strip('\'')

def __docker_start():
    _exec_system("echo '' > /tmp/hosts")
    (distro, release, codename) = lsb.lsb_release()
    for i in dock_list:
        if distro == 'CentOS':
            errno = _exec_system("docker exec %s sed -i 's/^UsePAM yes$/UsePAM no/g' /etc/ssh/sshd_config" % (i))
            if errno:
                _derror("Please install docker last version")
                exit(errno)

            errno = _exec_system("docker exec %s sed -i 's/^#PermitRootLogin yes$/PermitRootLogin yes/g' /etc/ssh/sshd_config" % (i))
            if errno:
                _derror("Please install docker last version")
                exit(errno)

        errno = _exec_system("docker exec %s bash -c \"echo 'mdsmds' | passwd root --stdin\"" % (i))
        if errno:
            _derror("Please install docker last version")
            exit(errno)

        _exec_system(r"docker exec %s sed -i 's/\\h \\W/\\H \\W/g' /etc/bashrc" % (i))

        """
        errno = _exec_system('docker exec %s /etc/init.d/sshd start' % (i))
        if errno:
            _derror("Please install docker last version")
            exit(errno)
        """
        
        errno = _exec_system('docker exec %s /usr/bin/rsync --daemon --config=/etc/rsyncd.conf' % (i))
        if errno:
            _derror("Please install docker last version")
            exit(errno)
        _exec_system("echo %s    %s >> /tmp/hosts" % (get_addr(i), i))


    for i in dock_list:
        try:
            _put_remote(get_addr(i), "/tmp/hosts", "/etc/hosts", user='root', password='mdsmds', timeout=10)
        except Exception, e:
            _derror("put file /tmp/hosts to %s failed, please check sshd is running normal" % i)
            #_exec_system('docker exec %s bash -c \
            #        "sed -i \'s/session    required     pam_loginuid.so/#session    required     pam_loginuid.so/\' /etc/pam.d/sshd"' % i)
            exit(1)

def docker_run():
    for i in dock_list:
        #_exec_system('docker run --name %s -h %s -d -t %s /bin/bash' % (i, i, tpl))
        _exec_system('docker run --name %s -h %s -v /opt/fusionstack --privileged=true -ti -v /sys/fs/cgroup:/sys/fs/cgroup:ro -d -t %s' % (i, i, tpl))

    __docker_start()

def docker_stop():
    for i in dock_list:
        _exec_system('docker stop %s' % (i))

def docker_start():
    _exec_system("sysctl -e kernel.core_pattern=/tmp/core/core-%e-%p-%s")

    for i in dock_list:
        _exec_system('docker start %s' % (i))

    __docker_start()

def docker_remove():
    for i in dock_list:
        _exec_system('docker rm %s' % (i))

class Test():
    def __init__(self):
        os.system("echo 2097152 > /proc/sys/fs/aio-max-nr")

    def cleanup(self, keeplich = False):
        _set_value("/tmp/pool_idx", "0")

        for i in dock_list:
            errno = _exec_system('docker exec %s echo "%s cleanup start..."' % (i, i))
            if errno:
                exit(errno)
            _exec_system('docker exec %s  pkill -9 lichd' % (i))
            _exec_system('docker exec %s  pkill -9 lich.inspect' % (i))
            _exec_system('docker exec %s  pkill -9 redis-server' % (i))
            _exec_system('docker exec %s  rm -rf /opt/fusionstack/data' % (i))
            _exec_system('docker exec %s  rm -rf /opt/fusionstack/log/' % (i))
            if not keeplich:
                _exec_system('docker exec %s  rm -rf /opt/fusionstack/lich/' % (i))
                _exec_system('docker exec %s  rm -rf /opt/fusionstack/etc/' % (i))
            else:
                _exec_system('docker exec %s  rm -rf /opt/fusionstack/etc/cluster.conf' % (i))
            _exec_system('docker exec %s  rm -rf /dev/shm/lich4' % (i))
            _exec_system('docker exec %s rm -rf /tmp/core' % (i))
            _exec_system('docker exec %s mkdir -p /tmp/core' % (i))
            _exec_system('docker exec %s mkdir -p /opt/fusionstack/data/' % (i))
            _exec_system('docker exec %s mkdir -p /opt/fusionstack/etc/' % (i))
            _exec_system('docker exec %s mkdir -p /opt/fusionstack/tmp/' % (i))
            _exec_system('docker exec %s mkdir -p /opt/fusionstack/data/disk/disk' % (i))
            _exec_system('docker exec %s mkdir -p /opt/fusionstack/data/disk/tier' % (i))
            _exec_system('docker exec %s systemctl stop etcd' % (i))
            _exec_system('docker exec %s  rm -rf /opt/fusionstack/data/etcd/' % (i))

            _exec_system('docker exec %s touch  /opt/fusionstack/data/fake' % (i))
            _exec_system('docker exec %s echo 3 > /proc/sys/vm/drop_caches' % (i))
            #_exec_system('docker exec %s bash -c "echo -n \'cluster=xxxx;node=lich_testx;type=new;disk=1;pool=pool1;cache=0;cached=0;\' >  /opt/fusionstack/tmp/1.img"' % (i))
            #_exec_system('docker exec %s bash -c "dd if=/opt/fusionstack/tmp/1.img of=/opt/fusionstack/data/disk/disk/1.file bs=1 seek=1612"' % (i))
            #_exec_system('docker exec %s truncate  /opt/fusionstack/data/disk/disk/0.file --size 10G' % (i))
            #_exec_system('docker exec %s truncate  /opt/fusionstack/data/disk/disk/1.file --size 10G' % (i))
            #_exec_system('docker exec %s truncate  /opt/fusionstack/data/disk/disk/2.file --size 1G' % (i))
            #_exec_system('docker exec %s truncate  /opt/fusionstack/data/disk/disk/3.file --size 1G' % (i))
            #_exec_system('docker exec %s truncate  /opt/fusionstack/data/disk/disk/4.file --size 1G' % (i))
            #_exec_system('docker exec %s ln -s /opt/fusionstack/data/disk/disk/0.file /opt/fusionstack/data/disk/disk/0.disk' % (i))
            #_exec_system('docker exec %s ln -s /opt/fusionstack/data/disk/disk/1.file /opt/fusionstack/data/disk/disk/1.disk' % (i))
            #_exec_system('docker exec %s ln -s /opt/fusionstack/data/disk/disk/2.file /opt/fusionstack/data/disk/disk/2.disk' % (i))
            #_exec_system('docker exec %s ln -s /opt/fusionstack/data/disk/disk/3.file /opt/fusionstack/data/disk/disk/3.disk' % (i))
            #_exec_system('docker exec %s ln -s /opt/fusionstack/data/disk/disk/4.file /opt/fusionstack/data/disk/disk/4.disk' % (i))
            #_exec_system('docker exec %s bash -c "echo 0 >  /opt/fusionstack/data/disk/tier/0.tier"' % (i))
            #_exec_system('docker exec %s bash -c "echo 1 >  /opt/fusionstack/data/disk/tier/1.tier"' % (i))

    def sync(self):
        addr = get_addr(dock_list[0])
        newpath = os.path.abspath(path + '/../')
        cmd = 'rsync -varz --progress --no-o --no-g --exclude-from=%s/.gitignore %s root@%s::lich' %(newpath, newpath, addr)
        errno = _exec_system(cmd)
        if errno:
            _derror("cmd %s fail:%d" %(cmd, errno))
            exit(errno)

        cmd = "docker exec %s sed -i 's/#if 1 \/\*have static assert\*\//#if 0 \/\*have static assert\*\//g' /tmp/lich4/config/include/configure.h" %  (dock_list[0])
        errno = _exec_system(cmd)

        cmd = "docker exec %s sed -i 's/ENABLE_HUGE_MALLOC 1/ENABLE_HUGE_MALLOC 0/g' /tmp/lich4/config/include/configure.h" %  (dock_list[0])
        errno = _exec_system(cmd)
        """"
        cmd = "docker exec %s sed -i 's/ENABLE_HUGE_RING 1/ENABLE_HUGE_RING 0/g' /tmp/lich4/config/include/configure.h" %  (dock_list[0])
        errno = _exec_system(cmd)
        """
        cmd = "docker exec %s sed -i 's/ENABLE_CHUNK_DEBUG[^$]*$/ENABLE_CHUNK_DEBUG TRUE/g' /tmp/lich4/config/include/configure.h" %  (dock_list[0])
        errno = _exec_system(cmd)

        cmd = "docker exec %s sed -i 's/MAX_CHUNK_DEFAULT[^$]*$/MAX_CHUNK_DEFAULT (1024)/g' /tmp/lich4/config/include/configure.h" %  (dock_list[0])
        errno = _exec_system(cmd)

        cmd = "docker exec %s sed -i 's/127.0.0.0/%s/g' /tmp/lich4/etc/lich.conf.test" % (dock_list[0], addr)
        errno = _exec_system(cmd)

        cmd = "docker exec %s sed -i 's/#clustername XXX/clustername testcluster/g' /tmp/lich4/etc/lich.conf.test"% (dock_list[0])
        errno = _exec_system(cmd)
        if errno:
            _derror("cmd %s fail:%d" %(cmd, errno))
            exit(errno)

    def conf(self):
        cmd = 'docker exec %s rm -rf /tmp/lich4/cmakebuild' % (dock_list[0])
        errno = _exec_system(cmd)
        if errno:
            _derror("cmd %s fail:%d" %(cmd, errno))
            exit(errno)
            
        cmd = 'docker exec %s mkdir -p /tmp/lich4/cmakebuild' % (dock_list[0])
        errno = _exec_system(cmd)
        if errno:
            _derror("cmd %s fail:%d" %(cmd, errno))
            exit(errno)

        cmd = 'docker exec %s cmake -H/tmp/lich4 -B/tmp/lich4/cmakebuild -Dspdk=OFF -Dec=OFF' % (dock_list[0])
        errno = _exec_system(cmd)
        if errno:
            _derror("cmd %s fail:%d" %(cmd, errno))
            exit(errno)

        cmd = 'docker exec %s make -C /tmp/lich4/cmakebuild version' % (dock_list[0])
        errno = _exec_system(cmd)
        if errno:
            _derror("cmd %s fail:%d" %(cmd, errno))
            exit(errno)

        cmd = 'docker exec %s make -j5 -C /tmp/lich4/cmakebuild' % (dock_list[0])
        errno = _exec_system(cmd)
        if errno:
            _derror("cmd %s fail:%d" %(cmd, errno))
            exit(errno)

    def build(self, conf=True):
        cmd = 'docker exec %s make -j5 -C /tmp/lich4/cmakebuild install' % (dock_list[0])
        errno = _exec_system(cmd)
        if (conf):
            cmd = "docker exec %s cp /tmp/lich4/etc/lich.conf.test /opt/fusionstack/etc/lich.conf"% (dock_list[0])
            errno = _exec_system(cmd)
        if errno:
            _derror("cmd %s fail:%d" %(cmd, errno))
            exit(errno)

    def deploy(self):
        lst = ""
        for i in dock_list:
            lst = lst + (i + " ")

        cmd = 'docker exec %s /opt/fusionstack/lich/bin/lich create %s ' % (dock_list[0], lst)
        errno = _exec_system(cmd)
        if errno:
            _derror("cmd %s fail:%d" %(cmd, errno))
            exit(errno)

        """
        cmd = 'docker exec %s /opt/fusionstack/lich/libexec/lich.admin --poolcreate default' % (dock_list[0])
        errno = _exec_system(cmd)
        if errno:
            _derror("cmd %s fail:%d" %(cmd, errno))
            exit(errno)

        cmd = 'docker exec %s /opt/fusionstack/lich/libexec/lich.admin --poolcreate pool1' % (dock_list[0])
        errno = _exec_system(cmd)
        if errno:
            _derror("cmd %s fail:%d" %(cmd, errno))
            exit(errno)

        for i in dock_list:
            _exec_system('docker exec %s bash -c "mkdir -p /dev/shm/lich4/msgctl; echo 1 > /dev/shm/lich4/msgctl/backtrace ; echo 0 > /dev/shm/lich4/msgctl/sub/storage"' % (i))
        """

    def testall(self, conf, nofail, valgrind):
        fail = Fail(30, valgrind)

        #docker_add_config()
        self.cleanup()
        self.sync()
        if (conf):
            self.conf()
        self.build()
        self.deploy()
        _exec_system("mkdir -p /tmp/core/")
        _exec_system("sysctl -e kernel.core_pattern=/tmp/core/core-%e-%p-%s")

        fail.start(nofail)
        #_exec_system("python %s/test_list.py --length 5" % (path))

        p = subprocess.Popen("python %s/test_list.py --length 5 --valgrind %d" % (path, valgrind), shell=True)
        try:
            ret = p.wait()
            if (ret == 0):
                res = (p.communicate()[0])
                fail.stop(nofail)
                print ("testall success")
                return
            else:
                fail_exit("test fail")
        except KeyboardInterrupt as err:
            fail_exit("interrupted")

    def createcluster(self, conf):
        self.cleanup()
        self.sync()
        self.conf()
        self.build()
        self.deploy()

    def cleancluster(self, conf):
        self.cleanup(True)

    def onlyconf(self):
        self.cleanup()
        self.sync()
        self.conf()

    def updatecode(self, conf):
        """
        cmd = 'docker exec %s /opt/fusionstack/lich/bin/lich stop' % (dock_list[0])
        _exec_system(cmd)
        """

        for i in dock_list:
            _exec_system('docker exec %s echo 3 > /proc/sys/vm/drop_caches' % (i))
        
        cmd = 'docker exec %s cp /opt/fusionstack/etc/lich.conf /tmp/lich.conf' % (dock_list[0])
        _exec_system(cmd)
        self.sync()
        if (conf):
            self.conf()
        self.build(False)
        cmd = 'docker exec %s cp /tmp/lich.conf /opt/fusionstack/etc/lich.conf' % (dock_list[0])
        _exec_system(cmd)

        """
        cmd = 'docker exec %s /opt/fusionstack/lich/bin/lich update lich' % (dock_list[0])
        _exec_system(cmd)

        cmd = 'docker exec %s /opt/fusionstack/lich/bin/lich start' % (dock_list[0])
        _exec_system(cmd)
        """

def main():
    test = False
    conf = False
    createcluster = False
    cleancluster = False
    updatecode = False
    nofail = False
    valgrind = False

    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            'hv', ['pull', 'build', 'run', 'test', 'conf', 'stop', 'remove', 'start', 'list', 'createcluster', 'cleancluster', 'updatecode', 'nofail', 'valgrind']
            )
    except getopt.GetoptError, err:
        print str(err)
        usage()
        exit(errno.EINVAL)

    for o, a in opts:
        if o in ('--help'):
            usage()
            exit(0)
        elif o == '--pull':
            docker_pull()
        elif o == '--build':
            docker_build()
        elif o == '--run':
            docker_run()
        elif o == '--stop':
            docker_stop()
        elif o == '--remove':
            docker_remove();
        elif o == '--test':
            test = True
        elif o == '--conf':
            conf = True
        elif o == '--start':
            docker_start()
        elif o == '--list':
            docker_list()
        elif o == '--createcluster':
            createcluster = True
        elif o == '--cleancluster':
            cleancluster = True
        elif o == '--updatecode':
            updatecode = True
        elif o == '--nofail':
            nofail = True
        elif o == '--valgrind':
            valgrind = True
        else:
            usage()
            exit(errno.EINVAL)

    if (test):
        test = Test()
        test.testall(conf, nofail, valgrind)
    elif (createcluster):
        test = Test()
        test.createcluster(conf)
    elif (cleancluster):
        test = Test()
        test.cleancluster(conf)
    elif (updatecode):
        test = Test()
        test.updatecode(conf)
    elif (conf):
        test = Test()
        test.onlyconf()

if __name__ == '__main__':
    if (len(sys.argv) == 1):
        usage()
    else:
        main()
